<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta http-equiv="Content-Style-Type" content="text/css">
  <title></title>
  <meta name="Generator" content="Cocoa HTML Writer">
  <meta name="CocoaVersion" content="824.41">
  <style type="text/css">
    p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco; color: #bf0000}
    p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Helvetica; min-height: 22.0px}
    p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Helvetica}
    p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica}
    p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px}
    p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco; min-height: 12.0px}
    p.p7 {margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco}
    span.s1 {color: #000000}
    span.s2 {color: #bf0000}
    span.s3 {color: #0000bf}
    span.s4 {color: #606060}
    span.Apple-tab-span {white-space:pre}
  </style>
</head>
<body>
<p class="p1">//SLUGens released under the GNU GPL as extensions for SuperCollider 3, by Nick Collins, http://www.informatics.sussex.ac.uk/users/nc81/</p>
<p class="p2"><b></b><br></p>
<p class="p3"><b>LTI<span class="Apple-tab-span">	</span><span class="Apple-tab-span">	</span><span class="Apple-tab-span">	</span>Linear Time Invariant General Filter Equation</b></p>
<p class="p2"><b></b><br></p>
<p class="p4"><b>LTI.ar(input, bufnuma, bufnumb, mul, add)</b></p>
<p class="p5"><br></p>
<p class="p4">Represents the general LTI filter difference equation in the time domain:</p>
<p class="p5"><br></p>
<p class="p4">y(n) = b0x(n) + b1x(n-1) + ... + b(Nb)x(n-Nb) + a1y(n-1) + ... + a(Na)y(n-Na)</p>
<p class="p5"><br></p>
<p class="p4">This is not a pole/zero view, so you'd need to calculate time domain coefficients yourself if you want to work from z-plane backwards. A corollary is, stability is not guaranteed. This is part of the fun?</p>
<p class="p5"><br></p>
<p class="p4">You need to pass in the coefficients via two buffers, of arbitrary size.</p>
<p class="p5"><br></p>
<p class="p4"><b>input</b>- What do you want to filter?</p>
<p class="p4"><b>bufnuma</b>- Feedback filter coefficients, from previous outputs</p>
<p class="p4"><b>bufnumb</b>- Feedforward filter coefficients, from previous inputs</p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p7">(</p>
<p class="p1"><span class="s1">a=[0.02,-0.01]; </span>//feedback coefficients</p>
<p class="p7">b=[1,0.7,0,0,0,0,-0.8,0,0,0,0,0.9,0,0,0,-0.5,0,0,0,0,0,0,0.25,0.1,0.25]; <span class="s2">//feedforward coefficients</span></p>
<p class="p7">c=<span class="s3">Buffer</span>.sendCollection(s, a, 1);</p>
<p class="p7">d=<span class="s3">Buffer</span>.sendCollection(s, b, 1);</p>
<p class="p7">)</p>
<p class="p6"><br></p>
<p class="p7">{<span class="s3">LTI</span>.ar(<span class="s3">AudioIn</span>.ar,c.bufnum, d.bufnum)}.play</p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p1">//Note- you cannot update buffers during playback unless you stay within the initially allocated sizes</p>
<p class="p6"><br></p>
<p class="p7">(</p>
<p class="p1"><span class="s1">a=</span><span class="s3">Array</span><span class="s1">.fill(100,{0.0}); </span>//feedback coefficients</p>
<p class="p1"><span class="s1">b=</span><span class="s3">Array</span><span class="s1">.rand(100,-0.5,0.5); </span>//feedforward coefficients</p>
<p class="p7">b[0]=1;</p>
<p class="p7">c=<span class="s3">Buffer</span>.sendCollection(s, a, 1);</p>
<p class="p7">d=<span class="s3">Buffer</span>.sendCollection(s, b, 1);</p>
<p class="p7">)</p>
<p class="p6"><br></p>
<p class="p7">{<span class="s3">LTI</span>.ar(<span class="s3">AudioIn</span>.ar,c.bufnum, d.bufnum)}.play</p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p7">(</p>
<p class="p1"><span class="s1">b=</span><span class="s3">Array</span><span class="s1">.rand(100,-0.5,0.5); </span>//feedforward coefficients</p>
<p class="p7">b[0]=1;</p>
<p class="p7">d.sendCollection(b);</p>
<p class="p7">)</p>
<p class="p6"><br></p>
<p class="p1">//may explode...</p>
<p class="p6"><br></p>
<p class="p7">(</p>
<p class="p7">10.do({<span class="s3">arg</span> i; a[100.rand]=rrand(-0.1,0.1)}); <span class="s2">//feedforward coefficients</span></p>
<p class="p7">c.sendCollection(a);</p>
<p class="p7">)</p>
<p class="p6"><br></p>
<p class="p1">//from a routine</p>
<p class="p7">(</p>
<p class="p7">e={<span class="s3">inf</span>.do {</p>
<p class="p6"><br></p>
<p class="p1"><span class="s1">b=</span><span class="s3">Array</span><span class="s1">.rand(100,-0.5,0.5); </span>//feedforward coefficients</p>
<p class="p7">b[0]=1;</p>
<p class="p7">d.sendCollection(b);</p>
<p class="p6"><br></p>
<p class="p7">0.1.wait; }}.fork</p>
<p class="p7">)</p>
<p class="p6"><br></p>
<p class="p7">e.stop;</p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p1">//Code for testing and trying coefficients:</p>
<p class="p6"><br></p>
<p class="p1">//given two arrays of filter coefficients, calculate an impulse response over 1024 samples, then the fft gives approximate frequency gain and phase response<span class="Apple-converted-space"> </span></p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p7">(</p>
<p class="p7"><span class="s3">var</span> size = 1024, real, imag, cosTable, complex;<span class="Apple-converted-space"> </span></p>
<p class="p7"><span class="s3">var</span> a,b;</p>
<p class="p7"><span class="s3">var</span> lastn,lastindex,num;</p>
<p class="p7"><span class="s3">var</span> y, max;</p>
<p class="p6"><br></p>
<p class="p7">a=[0.02,0.05,0,0,0.01]; <span class="s2">//feedback coefficients</span></p>
<p class="p6"><br></p>
<p class="p7">b=[1,1,-0.5,0,0,0,-0.6,0.7]; <span class="s2">//feedforward coefficients</span></p>
<p class="p6"><br></p>
<p class="p1">//check poles of a are inside the unit circle by factorising the complex polynomial?<span class="Apple-converted-space"> </span></p>
<p class="p1">//this procedure uses only a finite impulse response so may give fallacious results of stability</p>
<p class="p6"><br></p>
<p class="p7">num=a.size;</p>
<p class="p6"><br></p>
<p class="p7">lastn=<span class="s3">Array</span>.fill(num,{0});</p>
<p class="p6"><br></p>
<p class="p7">lastindex=0;</p>
<p class="p6"><br></p>
<p class="p7">real = <span class="s3">Signal</span>.fill(size, {<span class="s3">arg</span> i; <span class="Apple-converted-space"> </span></p>
<p class="p6"><br></p>
<p class="p7">y=if(i&lt;(b.size),{b[i]},{0});</p>
<p class="p6"><br></p>
<p class="p7">y= y+((a.collect({<span class="s3">arg</span> val,j;<span class="Apple-converted-space">  </span>val*(lastn.wrapAt(lastindex+num-1-j));})).sum);</p>
<p class="p6"><br></p>
<p class="p7">lastn[lastindex]=y;</p>
<p class="p6"><br></p>
<p class="p7">lastindex=(lastindex+1)%num;</p>
<p class="p6"><br></p>
<p class="p7">y</p>
<p class="p7">});</p>
<p class="p6"><br></p>
<p class="p7">imag = <span class="s3">Signal</span>.newClear(size);</p>
<p class="p6"><br></p>
<p class="p7">cosTable = <span class="s3">Signal</span>.fftCosTable(size);</p>
<p class="p6"><br></p>
<p class="p7">complex = fft(real, imag, cosTable);<span class="Apple-converted-space"> </span></p>
<p class="p6"><br></p>
<p class="p7">a=complex.postln;</p>
<p class="p7">[real, (complex.magnitude), (complex.phase) ].flop.flat</p>
<p class="p7"><span class="Apple-tab-span">	</span>.plot(<span class="s4">"fft"</span>, <span class="s3">Rect</span>(0,0, 1024 + 8, 500), numChannels: 3);</p>
<p class="p6"><br></p>
<p class="p7">max=0;</p>
<p class="p6"><br></p>
<p class="p7">y=complex.magnitude;</p>
<p class="p6"><br></p>
<p class="p7">y.do{<span class="s3">arg</span> val; if(val&gt;max,{max=val;})};</p>
<p class="p6"><br></p>
<p class="p7">max</p>
<p class="p7">)</p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p1">//how to create the arbitrary filter from its difference equation coefficients? Need a new UGen (LTI)- or use Csound</p>
<p class="p6"><br></p>
<p class="p7">(</p>
<p class="p7">a=[0.02,0.05,0,0,0.01]; <span class="s2">//feedback coefficients</span></p>
<p class="p7">b=[1,1,-0.5,0,0,0,-0.6,0.7]; <span class="s2">//feedforward coefficients</span></p>
<p class="p7">c=<span class="s3">Buffer</span>.sendCollection(s, a, 1);</p>
<p class="p7">d=<span class="s3">Buffer</span>.sendCollection(s, b, 1);</p>
<p class="p7">)</p>
<p class="p6"><br></p>
<p class="p7">{<span class="s3">Impulse</span>.ar(1)}.play</p>
<p class="p6"><br></p>
<p class="p7">{<span class="s3">LTI</span>.ar(<span class="s3">Impulse</span>.ar(1), c.bufnum, d.bufnum)}.play</p>
<p class="p6"><br></p>
<p class="p7">{<span class="s3">LTI</span>.ar(<span class="s3">AudioIn</span>.ar(1), c.bufnum, d.bufnum)}.play</p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p7">(</p>
<p class="p1"><span class="s1">a=[0.01,-0.01]; </span>//Array.fill(10,{rrand(0.001,0.01)}); //feedback coefficients</p>
<p class="p7">b=[1]++<span class="s3">Array</span>.fill(100,{exprand(0.1,1)}); <span class="s2">//feedforward coefficients</span></p>
<p class="p7">c=<span class="s3">Buffer</span>.sendCollection(s, a, 1);</p>
<p class="p7">d=<span class="s3">Buffer</span>.sendCollection(s, b, 1);</p>
<p class="p7">)</p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p1">//piercing, careful!</p>
<p class="p7">{<span class="s3">Saw</span>.ar(<span class="s3">LFNoise0</span>.kr(10,4000,5000))}.play</p>
<p class="p6"><br></p>
<p class="p7">{<span class="s3">LTI</span>.ar(<span class="s3">Saw</span>.ar(<span class="s3">LFNoise0</span>.kr(10,4000,5000)), c.bufnum, d.bufnum,0.1)}.play</p>
<p class="p6"><br></p>
<p class="p1">//Also see [Convolution]</p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p6"><br></p>
<p class="p6"><br></p>
</body>
</html>
